namespace Test.Robotics.Runtime
{
    using System;
    using System.Globalization;
    using Microsoft.Robotics.Tracing;
    using Microsoft.VisualStudio.TestTools.UnitTesting;

    /// <summary>
    /// Validates tracing library
    /// </summary>
    [TestClass]
    public class TracingTests
    {
        /// <summary>
        /// Tests if TraceOutFacadeCallsIntoMultipleTracers
        /// </summary>
        [Priority(0)]
        [TestMethod]
        [TestCategory("Unit")]
        [TestCategory("Tracing")]
        public void TraceOutFacadeCallsIntoMultipleTracers()
        {
            // Initialize dispatcher with 2 tracers
            var mockTracer1 = new MockTracer();
            var mockTracer2 = new MockTracer();
            SimpleTraceDispatcher testDispatcher = new SimpleTraceDispatcher(mockTracer1, mockTracer2);

            TraceOut.TraceDispatcher = testDispatcher;

            DateTime dt = DateTime.Now;

            mockTracer1.DateToUse = dt;
            mockTracer2.DateToUse = dt;

            // Do the tracing. String will be stored inside mock tracer
            TraceOut.Warning(TraceContexts.HRI, "Message");

            // Since we dont have a way to substitute sealed TraceContext class, we cannot use the formatter -
            // This code is copy-pasted from the SimpleFormatter.Format
            string expected = string.Concat(
                    dt.ToString("yy-MM-d H:mm:ss.fff\t", CultureInfo.InvariantCulture),
                    "WRN!\tHRI\tMessage");

            Assert.AreEqual<string>(mockTracer1.LastTrace, expected);
            Assert.AreEqual<string>(mockTracer2.LastTrace, expected);
        }

        /// <summary>
        /// Tests if SimpleFormatterCreatesTabSeparatedOutput
        /// </summary>
        [Priority(0)]
        [TestMethod]
        [TestCategory("Unit")]
        [TestCategory("Tracing")]
        public void SimpleFormatterCreatesTabSeparatedOutput()
        {
            DateTime dt = DateTime.Now;

            SimpleFormatter sf = new SimpleFormatter();

            string expected = string.Concat(
                    dt.ToString("yy-MM-d H:mm:ss.fff\t", CultureInfo.InvariantCulture),
                    "NAV\tHRI\tMessage 100");

            // Since we dont have a way to substitute sealed TraceContext class, we use Category context
            // instead of level. Its the same class, only string values are different, so it does not matter
            string actual = sf.Format(dt, TraceContexts.Nav, TraceContexts.HRI, "Message {0}", 100);

            Assert.AreEqual<string>(actual, expected);
        }

        /// <summary>
        /// Tests if SystemDiagnosticsWriterUsesFormatter
        /// </summary>
        [Priority(0)]
        [TestMethod]
        [TestCategory("Unit")]
        [TestCategory("Tracing")]
        public void SystemDiagnosticsWriterUsesFormatter()
        {
            DateTime dt = DateTime.Now;

            System.Diagnostics.TraceSwitch traceSwitch = new System.Diagnostics.TraceSwitch("HRI", "HRI trace context");

            MockFormatter mf = new MockFormatter();
            SystemDiagnosticsTraceWriter sdw = new SystemDiagnosticsTraceWriter(mf);

            // note, all trace levels default to info for test projects (the default if no trace level is given for a top level context in the app.config file)
            TraceContext errorTraceContext = new TraceContext((int)TraceLevelIDs.Error, "ERROR", null);
            sdw.WriteLine(dt, errorTraceContext, TraceContexts.HRI, "Message {0}", 100);

            Assert.AreEqual(dt, mf.Date);
            Assert.AreSame(errorTraceContext, mf.Level);
            Assert.AreSame(TraceContexts.HRI, mf.Context);
            Assert.AreSame("Message {0}", mf.FormatString);
            Assert.AreEqual<int>(100, (int)mf.Args[0]);
        }

        /// <summary>
        /// Tests if SimpleFormatterCreatesDotSeparatedNestedCategories
        /// </summary>
        [Priority(0)]
        [TestMethod]
        [TestCategory("Unit")]
        [TestCategory("Tracing")]
        public void SimpleFormatterCreatesDotSeparatedNestedCategories()
        {
            TraceContext category1 = TraceContexts.FromString(TraceContexts.Common, "SubCategory1");

            DateTime dt = DateTime.Now;

            string expected = string.Concat(
                    dt.ToString("yy-MM-d H:mm:ss.fff\t", CultureInfo.InvariantCulture),
                    "HRI\tCOMMON.SubCategory1\tMessage 100");

            SimpleFormatter sf = new SimpleFormatter();

            // Since we dont have a way to substitute sealed TraceContext class, we use Category context
            // instead of level. Its the same class, only string values are different, so it does not matter
            string actual = sf.Format(dt, TraceContexts.HRI, category1, "Message {0}", 100);

            Assert.AreEqual<string>(actual, expected);
        }

        /// <summary>
        /// Tests if SystemDiagnosticsWriter is filtering properly
        /// </summary>
        [Priority(0)]
        [TestMethod]
        [TestCategory("Unit")]
        [TestCategory("Tracing")]
        public void SystemDiagnosticsWriterFiltersProperly()
        {
            System.Diagnostics.TraceSwitch traceSwitch = new System.Diagnostics.TraceSwitch("HRI", "HRI trace context");

            MockFormatter mf = new MockFormatter();
            SystemDiagnosticsTraceWriter sdw = new SystemDiagnosticsTraceWriter(mf);

            // note, all trace levels default to info for test projects (the default if no trace level is given for a top level context in the app.config file)
            TraceContext offTraceContext = new TraceContext((int)TraceLevelIDs.Off, "OFF", null);
            TraceContext errorTraceContext = new TraceContext((int)TraceLevelIDs.Error, "ERROR", null);
            TraceContext warningTraceContext = new TraceContext((int)TraceLevelIDs.Warning, "WARNING", null);
            TraceContext infoTraceContext = new TraceContext((int)TraceLevelIDs.Information, "INFORMATION", null);
            TraceContext verboseTraceContext = new TraceContext((int)TraceLevelIDs.Verbose, "VERBOSE", null);

            TimeSpan oneSecond = new TimeSpan(0, 0, 1);

            DateTime dt = DateTime.Now;
            sdw.WriteLine(dt, errorTraceContext, TraceContexts.HRI, "Message {0}", 100);
            Assert.AreEqual(dt, mf.Date);

            dt += oneSecond;
            sdw.WriteLine(dt, warningTraceContext, TraceContexts.HRI, "Message {0}", 100);
            Assert.AreEqual(dt, mf.Date);

            dt += oneSecond;
            sdw.WriteLine(dt, infoTraceContext, TraceContexts.HRI, "Message {0}", 100);
            Assert.AreEqual(dt, mf.Date);

            dt += oneSecond;
            sdw.WriteLine(dt, verboseTraceContext, TraceContexts.HRI, "Message {0}", 100);
            Assert.AreNotEqual(dt, mf.Date);

            dt += oneSecond;
            sdw.WriteLine(dt, offTraceContext, TraceContexts.HRI, "Message {0}", 100);
            Assert.AreNotEqual(dt, mf.Date);

            // Now repreat the same tests but using a trace context with a sub context
            TraceContext subcontext = TraceContexts.FromString(TraceContexts.HRI, "subcontext");
            dt += oneSecond;
            sdw.WriteLine(dt, errorTraceContext, subcontext, "Message {0}", 100);
            Assert.AreEqual(dt, mf.Date);

            dt += oneSecond;
            sdw.WriteLine(dt, warningTraceContext, subcontext, "Message {0}", 100);
            Assert.AreEqual(dt, mf.Date);

            dt += oneSecond;
            sdw.WriteLine(dt, infoTraceContext, subcontext, "Message {0}", 100);
            Assert.AreEqual(dt, mf.Date);

            dt += oneSecond;
            sdw.WriteLine(dt, verboseTraceContext, subcontext, "Message {0}", 100);
            Assert.AreNotEqual(dt, mf.Date);

            dt += oneSecond;
            sdw.WriteLine(dt, offTraceContext, subcontext, "Message {0}", 100);
            Assert.AreNotEqual(dt, mf.Date);
        }
    }
}
